using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Threading;
using NUnit.Core;

using TestResult=Microsoft.VisualStudio.TestTools.Common.TestResult;
using Microsoft.VisualStudio.TestTools.Common;
using Microsoft.VisualStudio.TestTools.Execution;

using NUnitForVSTS.Common;

namespace NUnitForVSTS.NUnitHandling
{
    /// <summary>
    /// Executer for the mstest tests
    /// </summary>
	public class NUnitTestExecuter: IDisposable
	{
		private object _classInstance;
		private ConstructorInfo _constructor;
		private MethodInfo _fixtureSetupMethod;
		private MethodInfo _setupMethod;
		private MethodInfo _testMethod;
		private MethodInfo _teardownMethod;
		private MethodInfo _fixtureTeardownMethod;

		private Type _expectedException;
		private ConsoleOutputRedirector _consoleOutputRedirector;
		private object _redirectorLock;
		private TestResult _testResult;
		private NUnitTestRunner _testRunner;

		private static Dictionary<Assembly, SharedAssemblyInfo> SharedAssemblyInfoCache  = new Dictionary<Assembly, SharedAssemblyInfo>();

        private static Dictionary<Type, object> TestInstanceCache = new Dictionary<Type, object>();
		private static object TestInstanceCacheLock = new object();
		
		private INUnitTestElement _testElement;
		private PropertyInfo _testContextProperty;
		private Stopwatch _timer;
		private object _timerLock;
		private Type _fixtureClassType;
		private static Dictionary<string, Type> TypeCache = new Dictionary<string, Type>();
		private static object TypeCacheLock = new object();

        /// <summary>
        /// Initializes a new instance of the <see cref="NUnitTestExecuter"/> class.
        /// </summary>
        /// <param name="test">The test.</param>
		public NUnitTestExecuter(INUnitTestElement test)
		{
			_timerLock = new object();
			_redirectorLock = new object();
			_testElement = test;
		}

        /// <summary>
        /// Executes the specified result.
        /// </summary>
        /// <param name="result">The result.</param>
        /// <param name="runner">The runner.</param>
		public void Execute(TestResult result, NUnitTestRunner runner)
		{
			_testRunner = runner;
			try
			{
				_testResult = result;
				lock (_timerLock)
				{
					_timer = new Stopwatch();
					_timer.Start();
				}
				ResolveMethods();
				ResolveTestMethod();
				ExecuteTest();
				_testResult.Outcome = TestOutcome.Passed;
			}
            catch (NUnitException)
			{
				return;
			}
			catch (Exception exception)
			{
				Trace.TraceError("Unit Test Executor threw exception: " + exception);
				throw;
			}
			finally
			{
				lock (_timerLock)
				{
					SetResultDuration();
					_timer = null;
				}
			}
		}
        /// <summary>
        /// Resolves the methods.
        /// </summary>
		private void ResolveMethods()
		{
			try
			{
				_fixtureClassType = GetType(_testElement.ClassTypeName);
			}
			catch (Exception exception)
			{
				_testResult.Outcome = TestOutcome.Error;
				_testResult.ErrorMessage = string.Format(CultureInfo.CurrentUICulture,"Could not load a fixture type: {0}, {1}", _testElement.ClassTypeName, exception);
                throw new NUnitException();
			}
			SharedTypeInfo sharedTypeInfo = GetTypeInfo(_fixtureClassType);
			string errorMessage = sharedTypeInfo.Error;
            if (string.IsNullOrEmpty(errorMessage))
            {
                errorMessage = sharedTypeInfo.AssemblyInfo.Error;
            }

			if (!string.IsNullOrEmpty(errorMessage))
			{
				_testResult.Outcome = TestOutcome.Error;
				_testResult.ErrorMessage = errorMessage;
                throw new NUnitException(errorMessage);
			}
			_setupMethod = sharedTypeInfo.TestInitializer;
			_teardownMethod = sharedTypeInfo.TestCleanup;
			_fixtureSetupMethod = sharedTypeInfo.ClassInitializer;
			_fixtureTeardownMethod = sharedTypeInfo.ClassCleanup;
			_testContextProperty = sharedTypeInfo.TestContextProperty;
			_constructor = sharedTypeInfo.Constructor;
		}

        /// <summary>
        /// Resolves the test method.
        /// </summary>
		private void ResolveTestMethod()
		{
			INUnitTestMethod testMethodInfo = _testElement.NUnitTestMethod;
			_testMethod = ResolveMethod(_testElement.NUnitTestMethod.Name, typeof(void));
			if (_testElement.CreatedByUI)
			{
                Attribute exceptionAttributes = _testMethod.GetAttribute(MemberInfoExtentions.ExpectedExceptionAttributeType);
				if (exceptionAttributes != null)
				{
					_expectedException = GetExpectedExceptionType(exceptionAttributes);
					if (!_expectedException.IsSubclassOf(typeof(Exception)))
					{
						_testResult.Outcome = TestOutcome.Error;
						_testResult.ErrorMessage = string.Format(CultureInfo.CurrentUICulture,"Expected exception type is not subclass of System.Exception: {0}, {1}", _fixtureClassType.FullName, _testElement.Name);
                        throw new NUnitException();
					}
				}
			}
			else if (testMethodInfo.ExpectedException != null)
			{
				try
				{
					_expectedException = GetType(testMethodInfo.ExpectedException);
				}
				catch (Exception exception)
				{
					_testResult.Outcome = TestOutcome.Error;
					_testResult.ErrorMessage = string.Format(CultureInfo.CurrentUICulture,"Could not load Exception type: {0}, {1}", testMethodInfo.ExpectedException, exception);
                    throw new NUnitException();
				}
			}
		}

        /// <summary>
        /// Gets the expected type of the exception.
        /// </summary>
        /// <param name="expectedExceptionAttribute">The expected exception attribute.</param>
        /// <returns></returns>
		private static Type GetExpectedExceptionType(Attribute expectedExceptionAttribute)
		{
			Type type = expectedExceptionAttribute.GetType();
			PropertyInfo propertyInfo = type.GetProperty("ExceptionType");
			return (Type)propertyInfo.GetValue(expectedExceptionAttribute, null);
		}

        /// <summary>
        /// Resolves the method.
        /// </summary>
        /// <param name="methodName">Name of the method.</param>
        /// <param name="returnType">Type of the return.</param>
        /// <returns></returns>
		private MethodInfo ResolveMethod(string methodName, Type returnType)
		{
			MethodInfo resolvedTestMethod;
			try
			{
				resolvedTestMethod = _fixtureClassType.GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance,
					null, Type.EmptyTypes, null);
			}
			catch (AmbiguousMatchException ambiguousMatchException)
			{
				_testResult.Outcome = TestOutcome.Error;
				_testResult.ErrorMessage = string.Format(CultureInfo.CurrentUICulture,"More than one method found: {0}, {1}, {2}", _testElement.FullyQualifiedClassName, methodName, ambiguousMatchException.Message);
                throw new NUnitException();
			}
			if (resolvedTestMethod == null)
			{
				_testResult.Outcome = TestOutcome.Error;
				_testResult.ErrorMessage = string.Format(CultureInfo.CurrentUICulture,"No method found: {0}, {1}", _testElement.ClassTypeName, methodName);
                throw new NUnitException();
			}
			if (resolvedTestMethod.ReturnType != returnType)
			{
				_testResult.Outcome = TestOutcome.Error;
				_testResult.ErrorMessage = string.Format(CultureInfo.CurrentUICulture,"Method has wrong signature: {0}, {1}, {2}", _testElement.ClassTypeName, methodName, "UTA_MethodHasWrongSignatureResolution_ReturnType(returnType))");
                throw new NUnitException();
			}
			return resolvedTestMethod;
		}

        /// <summary>
        /// Executes the test.
        /// </summary>
		private void ExecuteTest()
		{
			CreateTestClassInstance();
			RunClassInitializeMethod();
			try
			{
				lock (_redirectorLock)
				{
					_consoleOutputRedirector = new ConsoleOutputRedirector();
				}
				RunInitializeMethod();
				try
				{
					RunTestMethod();
				}
				finally
				{
					RunCleanupMethod();
				}
			}
			finally
			{
				GetConsoleOutput();
			}
		}

        /// <summary>
        /// Sets the duration of the result.
        /// </summary>
		private void SetResultDuration()
		{
			lock (_timerLock)
			{
				if (_timer != null)
				{
					_timer.Stop();
					_testResult.Duration = _timer.Elapsed;
				}
			}
		}

        /// <summary>
        /// Gets the type info.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <returns></returns>
		private SharedTypeInfo GetTypeInfo(Type type)
		{
			SharedAssemblyInfo assemblyInfo = GetAssemblyInfo(type.Assembly);
			SharedTypeInfo sharedTypeInfo = assemblyInfo.Types[type];
			if (sharedTypeInfo.Constructor == null)
			{
				sharedTypeInfo.Constructor = _fixtureClassType.GetConstructor(Type.EmptyTypes);
				if (sharedTypeInfo.Constructor == null)
				{
					_testResult.Outcome = TestOutcome.Error;
					_testResult.ErrorMessage = string.Format(CultureInfo.CurrentUICulture,"No default constructor found: {0}", _testElement.FullyQualifiedClassName);
                    throw new NUnitException();
				}
			}
			return sharedTypeInfo;
		}

        /// <summary>
        /// Gets the assembly info.
        /// </summary>
        /// <param name="assembly">The assembly.</param>
        /// <returns></returns>
		private static SharedAssemblyInfo GetAssemblyInfo(Assembly assembly)
		{
			SharedAssemblyInfo assemblyInfo;
			lock (SharedAssemblyInfoCache)
			{
				if (SharedAssemblyInfoCache.TryGetValue(assembly, out assemblyInfo))
				{
					return assemblyInfo;
				}
			}
			assemblyInfo = new SharedAssemblyInfo();
			foreach (Type type in assembly.GetTypes())
			{
				SharedTypeInfo typeInfo = new SharedTypeInfo();
				typeInfo.AssemblyInfo = assemblyInfo;
				assemblyInfo.Types.Add(type, typeInfo);

                if (!type.HasAttribute(MemberInfoExtentions.TestFixtureAttributeType))
				{
					typeInfo.Error = "Not a test";
				}
				else
				{
					BindingFlags attr = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance;
					foreach (MethodInfo method in type.GetMethods(attr))
					{
                        ValidationResult result;
                        
                        result = ValidateMethod(MemberInfoExtentions.SetupAttributeType, method,Type.EmptyTypes, typeof(void), true, false, true, typeInfo.TestInitializer);
                        if (result == ValidationResult.Ok)
                        {
                            typeInfo.TestInitializer = method;
                        }
                        else
                        {
                            typeInfo.Error = GetValidationError("Setup", result);
                        }
                        
                        result = ValidateMethod(MemberInfoExtentions.TeardownAttributeType, method, Type.EmptyTypes, typeof(void), true, false, true, typeInfo.TestCleanup);
                        if (result == ValidationResult.Ok)
                        {
                            typeInfo.TestCleanup = method;
                        }
                        else
                        {
                            typeInfo.Error = GetValidationError("Teardown", result);
                        }
                        result = ValidateMethod(MemberInfoExtentions.TestFixtureSetupAttributeType, method, Type.EmptyTypes, typeof(void), true, false, true, typeInfo.ClassInitializer);
                        if (result == ValidationResult.Ok)
                        {
                            typeInfo.ClassInitializer = method;
                        }
                        else
                        {
                            typeInfo.Error = GetValidationError("FixtureSetup", result);
                        }
                        result = ValidateMethod(MemberInfoExtentions.TestFixtureTeardownAttributeType, method, Type.EmptyTypes, typeof(void), true, false, true, typeInfo.ClassCleanup);
                        if (result == ValidationResult.Ok)
                        {
                            typeInfo.ClassCleanup = method;
                        }
                        else
                        {
                            typeInfo.Error = GetValidationError("FixtureTearDown", result);
                        }
					}
				}
			}
			lock (SharedAssemblyInfoCache)
			{
				SharedAssemblyInfoCache[assembly] = assemblyInfo;
			}
			return assemblyInfo;
		}

        /// <summary>
        /// Gets the validation error.
        /// </summary>
        /// <param name="methodType">Type of the method.</param>
        /// <param name="result">The result.</param>
        /// <returns></returns>
        private static string GetValidationError(string methodType, ValidationResult result)
        {
            const string duplicateError = "Multiple {0} methods found";
            const string invalidSignature = "Invalid signature of method: {0}"; 

            switch (result)
            {
                case ValidationResult.DuplicateError:
                    return string.Format(CultureInfo.CurrentCulture, duplicateError, methodType);
                case ValidationResult.WrongSignature:
                    return string.Format(CultureInfo.CurrentCulture, invalidSignature, methodType); 
                case ValidationResult.Ok:
                    return null;
                default:
                    return null;
            }
        }

        /// <summary>
        /// Result of the method validation
        /// </summary>
        enum ValidationResult
        {
            /// <summary>
            /// Method is a duplicate
            /// </summary>
            DuplicateError,
            /// <summary>
            /// Method has wrons signature
            /// </summary>
            WrongSignature,
            /// <summary>
            /// Not Validated
            /// </summary>
            Ignored,
            /// <summary>
            /// Method is ok
            /// </summary>
            Ok
        }

        /// <summary>
        /// Validates the method.
        /// </summary>
        /// <param name="attributeType">Type of the attribute.</param>
        /// <param name="testMethod">The test method.</param>
        /// <param name="parameterTypes">The parameter types.</param>
        /// <param name="returnType">Type of the return.</param>
        /// <param name="isPublic">if set to <c>true</c> [is public].</param>
        /// <param name="isStatic">if set to <c>true</c> [is static].</param>
        /// <param name="inherited">if set to <c>true</c> [inherited].</param>
        /// <param name="method">The method.</param>
        /// <returns></returns>
        /// <remarks>public = true, isStatic = true, inheareted = true ???</remarks>
		private static ValidationResult ValidateMethod(string attributeType, MethodInfo testMethod, Type[] parameterTypes, Type returnType, bool isPublic, bool isStatic, bool inherited, MethodInfo method)
		{
			if (testMethod.HasAttribute(attributeType, inherited))
			{
				if (method != null)
				{
                    return ValidationResult.DuplicateError;
				}
                if (testMethod.IsPublic != isPublic)
                {
                    return ValidationResult.WrongSignature;
                }
                else if (testMethod.IsStatic != isStatic)
                {
                    return ValidationResult.WrongSignature;
                }
                else if (testMethod.ReturnType != returnType)
                {
                    return ValidationResult.WrongSignature;
                }
                else
                {
                    ParameterInfo[] infoArray1 = testMethod.GetParameters();
                    if (infoArray1.Length != parameterTypes.Length)
                    {
                        return ValidationResult.WrongSignature;
                    }
                    else if (infoArray1.Length != 0)
                    {
                        for (int num1 = 0; num1 < infoArray1.Length; num1++)
                        {
                            if (infoArray1[num1].ParameterType != parameterTypes[num1])
                            {
                                return ValidationResult.WrongSignature;
                            }
                        }
                    }
                }
                return ValidationResult.Ok;
			}
            return ValidationResult.Ignored;
		}

        /// <summary>
        /// Runs the class initialize method.
        /// </summary>
		private void RunClassInitializeMethod()
		{
			if ((_fixtureSetupMethod == null) && (_fixtureTeardownMethod != null))
			{
				if (!_testRunner.fixtureTeardownMethods.ContainsKey(_fixtureTeardownMethod))
				{
					_testRunner.fixtureTeardownMethods.Add(_fixtureTeardownMethod, new KeyValuePair<object, bool>(_classInstance, true));
				}
			}
			else if (_fixtureSetupMethod != null)
			{
				lock (_testRunner)
				{
					if (!_testRunner.fixtureSetup.ContainsKey(_fixtureSetupMethod))
					{
						_testRunner.fixtureSetup.Add(_fixtureSetupMethod, false);
						TraceExecution("FixtureSetup has failed: {0}, {1}", _fixtureSetupMethod.DeclaringType.FullName, _fixtureSetupMethod.Name);
						try
						{
							_fixtureSetupMethod.Invoke(_classInstance, new object[0]);
							_testRunner.fixtureSetup[_fixtureSetupMethod] = true;
						}
						catch (Exception exception1)
						{
							_testRunner.fixtureSetupExceptions.Add(_fixtureSetupMethod, exception1);
						}
						if ((_testRunner.fixtureSetup[_fixtureSetupMethod] && (_fixtureTeardownMethod != null)) && !_testRunner.fixtureTeardownMethods.ContainsKey(_fixtureTeardownMethod))
						{
							_testRunner.fixtureTeardownMethods.Add(_fixtureTeardownMethod, new KeyValuePair<object, bool>(_classInstance, true));
						}
					}
				}
				if (!_testRunner.fixtureSetup[_fixtureSetupMethod])
				{
					Exception fixtureSetupException = _testRunner.fixtureSetupExceptions[_fixtureSetupMethod];
					Exception innerException = fixtureSetupException.InnerException;
					if (innerException == null)
					{
						innerException = fixtureSetupException;
					}
					_testResult.Outcome = TestOutcome.Failed;
					_testResult.ErrorMessage = innerException.Message;
					_testResult.ErrorStackTrace = innerException.StackTrace;
                    throw new NUnitException();
				}
			}
		}

        /// <summary>
        /// Creates the test class instance.
        /// </summary>
		private void CreateTestClassInstance()
		{
			TraceExecution("Creating fixture instance: {0}", _testElement.ClassTypeName);
			try
			{
				TraceExecution("NUnitTestExecuter: Attempting to execute constructor of test class: {0}", _constructor.Name);

				lock (TestInstanceCacheLock)
				{
					if (!TestInstanceCache.TryGetValue(_constructor.DeclaringType, out _classInstance))
					{
						_classInstance = _constructor.Invoke(null);
						TestInstanceCache.Add(_constructor.DeclaringType, _classInstance);
					}
				}
				TraceExecution("NUnitTestExecuter: Successfully executed constructor of test class: {0}", _constructor.Name);
			}
			catch (Exception exception)
			{
				Trace.TraceInformation("NUnitTestExecuter: CreateTestClassInstance threw:\n" + exception);
				_testResult.Outcome = TestOutcome.Failed;
				_testResult.ErrorMessage = string.Format(CultureInfo.CurrentUICulture, "Instance creation error: {0}, {1}", _testElement.FullyQualifiedClassName, (exception.InnerException != null) ? exception.InnerException.Message : exception.Message);
				_testResult.ErrorStackTrace = (exception.InnerException != null) ? exception.InnerException.StackTrace : exception.StackTrace;
				_testResult.SystemException = exception;
                throw new NUnitException();
			}
			SharedTypeInfo info1 = GetTypeInfo(_fixtureClassType);
			if ((!info1.Used && (_testContextProperty != null)) && !_testContextProperty.CanWrite)
			{
				_testResult.AddTextMessage(string.Format(CultureInfo.CurrentUICulture, "Test context not writeable: {0}", _testElement.FullyQualifiedClassName));
				_testResult.Outcome = TestOutcome.Warning;
			}
			info1.Used = true;
		}

        /// <summary>
        /// Runs the initialize method.
        /// </summary>
		private void RunInitializeMethod()
		{
			if (_setupMethod != null)
			{
				TraceExecution(string.Format(CultureInfo.CurrentUICulture,"Setup method was called: {0}, {1}", _testElement.ClassTypeName, _setupMethod.Name));
				try
				{
					_setupMethod.Invoke(_classInstance, null);
				}
				catch (Exception testMethodException)
				{
					Exception innerException = testMethodException.InnerException;
					if (innerException == null)
					{
						innerException = testMethodException;
					}
					_testResult.Outcome = TestOutcome.Failed;
					_testResult.ErrorMessage = innerException.Message;
					_testResult.ErrorStackTrace = innerException.StackTrace;
                    throw new NUnitException();
				}
			}
		}

        /// <summary>
        /// Runs the test method.
        /// </summary>
		private void RunTestMethod()
		{
			TraceExecution(string.Format(CultureInfo.CurrentUICulture,"Run test method: {0}, {1}", _testElement.ClassTypeName, _testElement.NUnitTestMethod));
			bool gotError = false;
			try
			{
				_testMethod.Invoke(_classInstance, new object[0]);
			}
			catch (ThreadAbortException threadException)
			{
				if (!_testRunner.testStopped)
				{
					Thread.ResetAbort();
				}
				HandleMethodException(threadException);
				gotError = true;
			}
			catch (Exception exception)
			{
				HandleMethodException(exception);
				gotError = true;
			}
			if ((_expectedException != null) && !gotError)
			{
				_testResult.Outcome = TestOutcome.Failed;
				_testResult.ErrorMessage = string.Format(CultureInfo.CurrentUICulture,"Exception was expected, but was not catched: {0}, {1}, {2}", _testElement.ClassTypeName, _testElement.NUnitTestMethod.Name, _testElement.NUnitTestMethod.ExpectedException);
                throw new NUnitException();
			}
		}

        /// <summary>
        /// Runs the cleanup method.
        /// </summary>
		private void RunCleanupMethod()
		{
			if (_teardownMethod != null)
			{
				TraceExecution(string.Format(CultureInfo.CurrentUICulture,"Calling Teardown method: {0}, {1}", _testElement.ClassTypeName, _teardownMethod.Name));
				try
				{
					_teardownMethod.Invoke(_classInstance, null);
				}
				catch (Exception exception)
				{
					_testResult.Outcome = TestOutcome.Failed;
					if (string.IsNullOrEmpty(_testResult.ErrorMessage))
					{
						Exception innerException = exception.InnerException;
						if (innerException == null)
						{
							innerException = exception;
						}
						_testResult.Outcome = TestOutcome.Failed;
						_testResult.ErrorMessage = innerException.Message;
						_testResult.ErrorStackTrace = innerException.StackTrace;
                        throw new NUnitException();
					}
					return;
				}
			}
		}

        /// <summary>
        /// Traces the execution.
        /// </summary>
        /// <param name="msg">The MSG.</param>
        /// <param name="args">The args.</param>
		private void TraceExecution(string msg, params object[] args)
		{
			_testResult.TraceInfo.AppendTrace(string.Format(CultureInfo.CurrentUICulture, msg, args));
		}

        /// <summary>
        /// Gets the console output.
        /// </summary>
		public void GetConsoleOutput()
		{
			lock (_redirectorLock)
			{
				if (_consoleOutputRedirector != null)
				{
					_testResult.StdOut = _consoleOutputRedirector.StdOut;
					_testResult.StdErr = _consoleOutputRedirector.StdErr;
					_testResult.DebugTrace = _consoleOutputRedirector.DebugTrace;
					_consoleOutputRedirector.Dispose();
					_consoleOutputRedirector = null;
				}
			}
		}

        /// <summary>
        /// Handles the method exception.
        /// </summary>
        /// <param name="ex">The ex.</param>
		private void HandleMethodException(Exception ex)
		{
			if (_expectedException != null)
			{
				if (ex.InnerException != null)
				{
					Exception innerException = ex.InnerException;
					if ((innerException is TargetInvocationException) && !typeof(TargetInvocationException).IsAssignableFrom(_expectedException))
					{
						innerException = innerException.InnerException;
					}
					if ((innerException is TargetInvocationException) && !typeof(TargetInvocationException).IsAssignableFrom(_expectedException))
					{
						innerException = innerException.InnerException;
					}
					if (innerException.GetType() != _expectedException)
					{
						_testResult.Outcome = TestOutcome.Failed;
						_testResult.ErrorMessage = innerException.Message;
						_testResult.ErrorStackTrace = innerException.StackTrace;
						throw new NUnitException();
					}
					return;
				}
				_testResult.Outcome = TestOutcome.Failed;
				_testResult.ErrorMessage = string.Format(CultureInfo.CurrentUICulture,"UTA_TestMethodWrongException: {0}, {1}, {2}, {3}, {4}", _testElement.ClassTypeName, _testElement.NUnitTestMethod.Name, ex.GetType().FullName, _expectedException.FullName, ex.Message);
				_testResult.ErrorStackTrace = ex.StackTrace;
                throw new NUnitException();
			}
			_testResult.Outcome = TestOutcome.Failed;
			_testResult.ErrorMessage = ex.InnerException.Message;
			_testResult.ErrorStackTrace = ex.InnerException.StackTrace;
            throw new NUnitException();
		}

        /// <summary>
        /// Gets the type.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <returns></returns>
		private static Type GetType(string type)
		{
			Type cahedType;
			if (!TypeCache.TryGetValue(type, out cahedType))
			{
				lock (TypeCacheLock)
				{
					if(!TypeCache.TryGetValue(type, out cahedType))
					{
						cahedType = Type.GetType(type, true);
						TypeCache.Add(type, cahedType);
					}
				}
			}

			return cahedType;
		}

        /// <summary>
        /// Gets the partial result.
        /// </summary>
        /// <returns></returns>
		public TestResult PartialResult
		{
            get
            {
                SetResultDuration();
                GetConsoleOutput();
                return _testResult;
            }
		}

                #region IDisposable
        private bool _disposed;
        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        private void Dispose(bool disposing)
        {
            if (!(this._disposed)) {
                if (disposing)
                {
                    lock (_redirectorLock)
                    {
                        if (_consoleOutputRedirector != null)
                        {
                            _consoleOutputRedirector.Dispose();
                            _consoleOutputRedirector = null;
                        }
                    }
                }
            } 
            this._disposed = true;
        }
        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose() { 
            Dispose(true); 
            GC.SuppressFinalize(this); 
        }
        /// <summary>
        /// Releases unmanaged resources and performs other cleanup operations before the
        /// <see cref="NUnitTestExecuter"/> is reclaimed by garbage collection.
        /// </summary>
        ~NUnitTestExecuter() { 
            Dispose(false); 
        }
        #endregion
	}
}
